/*
 * Copyright (C) 2011-2013 GUIGUI Simon, fyhertz@gmail.com
 * 
 * This file is part of Spydroid (http://code.google.com/p/spydroid-ipcamera/)
 * 
 * Spydroid is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This source code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this source code; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package net.majorkernelpanic.streaming.rtp;

import android.util.Log;

import java.io.IOException;

/**
 *   RFC 4629.
 * 
 *   H.263 Streaming over RTP.
 *   
 *   Must be fed with an InputStream containing H.263 frames.
 *   The stream must start with mpeg4 or 3gpp header, it will be skipped.
 *   
 */
public class TSPacketizer extends AbstractPacketizer implements Runnable {

	public final static String TAG = "TSPacketizer";
	private final static int MAXPACKETSIZE = 188*7;
	private Statistics stats = new Statistics();

	private Thread t;

	public TSPacketizer() throws IOException {
		super();
		socket.setClockFrequency(90000);
	}

	public void start() throws IOException {
		if (t==null) {
			t = new Thread(this);
			t.start();
		}
	}

	public void stop() {
		try {
			is.close();
		} catch (IOException ignore) {}
		t.interrupt();
		t = null;

	}

	public void run() {
		long time, duration = 0;
		int i = 0, j = 0, bytes_read;
		boolean firstFragment = true;
		byte[] nextBuffer;
		stats.reset();


		try {
			while (!Thread.interrupted()) {

				if (j==0) buffer = socket.requestBuffer();
				socket.updateTimestamp(ts);



				time = System.nanoTime();
				bytes_read=fill(rtphl+j,MAXPACKETSIZE-rtphl-j);
				if (bytes_read<0) return;

				int last_byte = rtphl + j + bytes_read;
				duration += System.nanoTime() - time;
				j = 0;
				// Each h263 frame starts with: 0000 0000 0000 0000 1000 00??
				// Here we search where the next frame begins in the bit stream
				for (i=rtphl;i<last_byte;i+=188) {
					if (buffer[i]==0x47 && (buffer[i+1]&2)==2) {
						j=i;
						break;
					}
				}


				if (j>0) {
					// We send one RTCP Sender Report every 5 secs
					delta += duration/1000000;
					if (intervalBetweenReports>0) {
						if (delta>=intervalBetweenReports && duration/1000000>10) {
							delta = 0;
							report.send(System.nanoTime(),ts*90/1000000);
						}
					}
					// We have found the end of the frame
					stats.push(duration);
					ts+= stats.average(); duration = 0;
					//Log.d(TAG,"End of frame ! duration: "+stats.average());
					// The last fragment of a frame has to be marked
					socket.markNextPacket();
					buffer[1]|=0x80;
					send(j);
					nextBuffer = socket.requestBuffer();
					System.arraycopy(buffer,j,nextBuffer,rtphl,last_byte-j);
					buffer = nextBuffer;
					j = last_byte-j;

				} else {
					// We have not found the beginning of another frame
					// The whole packet is a fragment of a frame
					send(last_byte);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		Log.d(TAG,"TS Packetizer stopped !");

	}

	private int fill(int offset,int length) throws IOException {

		int sum = 0, len;
		long maxTimeMillis = System.currentTimeMillis() +500;
		while (sum<length-188){
			try {

				if (is.available() >= 188) {
					len = is.read(buffer, offset + sum, 188);
					sum += len;
					maxTimeMillis=System.currentTimeMillis() +500;
				} else if (System.currentTimeMillis() > maxTimeMillis&& sum>=188) {
					break;
				}
			}catch (IOException e){
				if (sum > 188) { break;}
			}

		};
		//Log.d(TAG,"sum:"+sum);
		return sum;

	}

	// The InputStream may start with a header that we need to skip
	private void skipHeader() throws IOException {
		// Skip all atoms preceding mdat atom
		byte[] buffer = new byte[3];
		while (true) {
			while (is.read() != 'm');
			is.read(buffer,0,3);
			if (buffer[0] == 'd' && buffer[1] == 'a' && buffer[2] == 't') break;
		}
	}

}
